package demo2

import (
	"container/list"
	"fmt"
)

/*
	遍历 list
	  1 . List导出了Back()和Front()返回结尾和开头元素：
		2. Element导出了两个方法Prev()和Next()用以访问前一个或后一个元素：
*/
// 正向遍历 List
func printListFromFront(col *list.List) {
	for e := col.Front(); e != nil; e = e.Next() {
		fmt.Print(e.Value, " ")
	}
	fmt.Println()
}

// 反向遍历 list
func printListFromEnd(col *list.List) {
	for e := col.Back(); e != nil; e = e.Prev() {
		fmt.Print(e.Value, "")
		fmt.Println()
	}
}

/*
	list（列表
	1. 列表是一种非连续存储的容器，由多个节点组成，节点通过一些变量记录彼此之间的关系。
			列表有多种实现方法，如单链表、双链表等
	2. 在 Go 语言中，将列表使用 container/list 包来实现，内部的实现原理是双链表。
			列表能够高效地进行任意位置的元素插入和删除操作。
	3. 列表与切片和 map 不同的是，列表并没有具体元素类型的限制。
			因此，列表的元素可以是任意类型。这既带来遍历，也会引来一些问题。
			给一个列表放入了非期望类型的值，在取出值后，将 interface{} 转换为期望类型时将会发生宕机。
*/

// TestDemo29 list 列表
func TestDemo29() {
	// list 的初始化有两种方法：New 和声明。两种方法的初始化效果都是一致的。
	// 1. 通过 container/list 包的 New 方法初始化 list
	list1 := list.New()

	// 2. 通过声明初始化list
	var list2 list.List

	fmt.Println(list1)
	printListFromFront(list1)
	fmt.Println(list2)

	/*
		在 list 中插入元素
			1. 双链表支持从队列前方或后方插入元素，分别对应的方法是 PushFront 和 PushBack。
			2. 这两个方法都会返回一个 *list.Element 结构。如果在以后的使用中需要删除插入的元素，
					则只能通过 *list.Element 配合 Remove() 方法进行删除，这种方法可以让删除更加效率化，也是双链表特性之一。
			3. 列表插入元素的方法:
				  3.1 InsertAfter(v interface {}, mark * Element) * Element
							在 mark 点之后插入元素， mark 点由其他插入函数提供
					3.2 InsertBefore(v interface {}, mark * Element) *Element
							在 mark 点之前插入元素，mark 点由其他插入函数提供
					3.3 PushBackList(other *List)
							添加 other 列表元素到尾部
					3.4  PushFrontList(other *List)
							添加 other 列表元素到头部
			4.

	*/

	// 添加元素到开头和结束
	list3 := list.New()
	list3.PushBack("last")
	list3.PushFront(999)
	fmt.Println(list3)
	printListFromFront(list3)

	// 插入元素到中间
	list4 := list.New()
	list4.PushBack("last")
	list4.PushFront("first")
	// PushFront 返回插入元素的 mark
	two := list4.PushFront(2)

	fmt.Println("添加位置", two)
	fmt.Println(list4)
	printListFromFront(list4)

	// 在 two 之前插入
	list4.InsertBefore("Before2", two)
	// 在 two 之后插入
	list4.InsertAfter("After2", two)

	fmt.Println("插入之后", list4)
	printListFromFront(list4)

	list5 := list.New()
	list5.PushFront("Golang")
	list5.PushBack(9999)
	// 添加 list 在 list4 之前
	list4.PushFrontList(list5)
	// 添加 list 在 list4 之后
	list4.PushBackList(list5)
	fmt.Println("添加list5之后")
	fmt.Println(list4)
	printListFromFront(list4)
	fmt.Println(list5)
	printListFromFront(list5)

	// 删除 元素
	list4.Remove(two)

	fmt.Println(list4)
	printListFromFront(list4)

	/*
		move 移动: 注意e和mark都必须是l的内的元素，否则该函数不执行任何动作。
			1. 移动e到mark之后  func (l *List) MoveAfter(e, mark *Element)
			2. 移动e到mark之前  func (l *List) MoveBefore(e, mark *Element)
			3. 移动e到末尾  func (l *List) MoveToBack(e *Element)
			4. 移动e到开头  func (l *List) MoveToFront(e *Element)
	*/
	list6 := list.New()

	middle := list6.PushFront("middle")
	middle1 := list6.PushFront("middle1")
	middle2 := list6.PushBack("middle2")
	front := list6.PushFront("front")
	end := list6.PushBack("end")

	printListFromFront(list6)
	// move 移动
	list6.MoveAfter(middle1, middle)
	list6.MoveBefore(middle2, middle)
	list6.MoveToBack(front)
	list6.MoveToFront(end)

	// 获取 list 长度
	fmt.Println("list6 长度", list6.Len())
	// 删除元素
	list6.Remove(middle)
	printListFromFront(list6)
	// 清除元素
	list6.Init()
	printListFromFront(list6)
}
